package org.xx.armory.commons;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

import static org.xx.armory.commons.Validators.greaterThanOrEqual;
import static org.xx.armory.commons.Validators.notNull;

public class LinkedTree<E>
        implements Tree<E> {
    private List<Node<E>> roots;

    public LinkedTree() {
        this.roots = new ArrayList<>();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<? extends Tree.Node<E>> roots() {
        return Collections.unmodifiableCollection(this.roots);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void clear() {
        this.roots.clear();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Node<E> add(
            E rootObj
    ) {
        final Node<E> node = new Node<>(this, null, rootObj, 1);
        if (this.roots.add(node)) {
            return node;
        } else {
            return null;
        }
    }

        private static class Node<E>
            implements Tree.Node<E> {
        private final E obj;
        private final List<Node<E>> children;
        private LinkedTree<E> tree;
        private Node<E> parent;
        private int depth;

        private Node(
                LinkedTree<E> tree,
                Node<E> parent,
                E obj,
                int depth
        ) {
            this.tree = notNull(tree, "tree");
            this.parent = parent;
            this.obj = obj;
            this.children = new ArrayList<>();
            this.depth = greaterThanOrEqual(depth, "depth", 1);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Tree<E> tree() {
            return this.tree;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public E get() {
            return this.obj;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Tree.Node<E> parent() {
            return this.parent;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Collection<Tree.Node<E>> children() {
            return Collections.unmodifiableCollection(this.children);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int depth() {
            return this.depth;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Tree.Node<E> add(
                E childObj
        ) {
            final Node<E> node = new Node<>(this.tree, this, childObj, this.depth + 1);
            if (this.children.add(node)) {
                return node;
            } else {
                return null;
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void delete(
                Tree.Node<E> child
        ) {
            this.children.removeIf(node -> Objects.equals(node, child));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return "(" + String.valueOf(this.obj) + ")" + super.toString();
        }
    }
}
